// TODO(mmalerba): this file should be split into separate cohesive partials for things like
//  "theming", "typography", "core". Currently splitting it is difficult because of our brittle
//  gulp-based release build process. We can update this when we switch to bazel.

@use '@material/feature-targeting' as mdc-feature-targeting;
@use '@material/typography' as mdc-typography;
@use '@material/theme/functions' as mdc-theme-functions;
@use '@material/theme/theme-color' as mdc-theme-color;
@use 'sass:map';
@use '../../material/core/style/layout-common';
@use '../../material/core/theming/theming';
@use '../../material/core/typography/typography';
@use '../../material/core/typography/typography-utils';

// A set of standard queries to use with MDC's queryable mixins.
$mat-base-styles-query: mdc-feature-targeting.without(mdc-feature-targeting.any(color, typography));
$mat-base-styles-without-animation-query:
    mdc-feature-targeting.all($mat-base-styles-query, mdc-feature-targeting.without(animation));
$mat-theme-styles-query: color;
$mat-typography-styles-query: typography;

// Mappings between Angular Material and MDC's typography levels.
// TODO: delete once all MDC components have migrated to using the 2018 mappings.
$mat-typography-2014-level-mappings: (
  mdc-to-mat: (
    headline1: display-4,
    headline2: display-3,
    headline3: display-2,
    headline4: display-1,
    headline5: headline,
    headline6: title,
    subtitle1: subheading-2,
    subtitle2: subheading-1,
    body1: body-2,
    body2: body-1,
    caption: caption,
    button: button,
    overline: null
  ),
  mat-to-mdc: (
    display-4: headline1,
    display-3: headline2,
    display-2: headline3,
    display-1: headline4,
    headline: headline5,
    title: headline6,
    subheading-2: subtitle1,
    subheading-1: subtitle2,
    body-2: body1,
    body-1: body2,
    caption: caption,
    button: button,
    input: null
  )
);

// Mappings between Angular Material and MDC's typography levels.
$mat-typography-2018-level-mappings: (
    // Maps from MDC typography levels (e.g. body1) to Angular Material typography levels
    // (e.g. body-1).
    mdc-to-mat: (
        headline1: headline-1,
        headline2: headline-2,
        headline3: headline-3,
        headline4: headline-4,
        headline5: headline-5,
        headline6: headline-6,
        subtitle1: subtitle-1,
        subtitle2: subtitle-2,
        body1: body-1,
        body2: body-2,
        caption: caption,
        button: button,
        overline: overline
    ),
    // Maps from Angular Material typography levels (e.g. body-1) to MDC typography levels
    // (e.g. body1).
    mat-to-mdc: (
        headline-1: headline1,
        headline-2: headline2,
        headline-3: headline3,
        headline-4: headline4,
        headline-5: headline5,
        headline-6: headline6,
        subtitle-1: subtitle1,
        subtitle-2: subtitle2,
        body-1: body1,
        body-2: body2,
        caption: caption,
        button: button,
        overline: overline
    )
);

// Converts an Angular Material typography level config to an MDC one.
@function mat-typography-level-config-to-mdc($mat-config, $mat-level) {
  $mappings: if(typography.private-typography-is-2018-config($mat-config),
      $mat-typography-2018-level-mappings, $mat-typography-2014-level-mappings);
  $mdc-level: map.get(map.get($mappings, mat-to-mdc), $mat-level);

  $result-with-nulls: map.merge(
      if($mdc-level,
          map.get(mdc-typography.$styles, $mdc-level),
          (
            text-decoration: none,
            -moz-osx-font-smoothing: grayscale,
            -webkit-font-smoothing: antialiased
          )),
      if($mat-level,
          (
            font-size: typography-utils.font-size($mat-config, $mat-level),
            line-height: typography-utils.line-height($mat-config, $mat-level),
            font-weight: typography-utils.font-weight($mat-config, $mat-level),
            letter-spacing: typography-utils.letter-spacing($mat-config, $mat-level),
            font-family: typography-utils.font-family($mat-config, $mat-level),
            // Angular Material doesn't use text-transform, so disable it.
            text-transform: none,
          ),
          ()));

  // We need to strip out any keys with a null value. Leaving them in will cause MDC to emit CSS
  // variables with no fallback value, which breaks some builds.
  $result: ();
  @each $property, $value in $result-with-nulls {
    @if $value != null {
      $result: map.merge($result, ($property: $value));
    }
  }
  @return $result;
}

// Converts an Angular Material typography config to an MDC one.
@function mat-typography-config-to-mdc($mat-config: typography.define-typography-config()) {
  $mdc-config: ();

  $mappings: if(typography.private-typography-is-2018-config($mat-config),
      $mat-typography-2018-level-mappings, $mat-typography-2014-level-mappings);

  @each $mdc-level, $mat-level in map.get($mappings, mdc-to-mat) {
    $mdc-config: map.merge(
        $mdc-config,
        ($mdc-level: mat-typography-level-config-to-mdc($mat-config, $mat-level)));
  }

  @return $mdc-config;
}

// Converts an MDC typography level config to an Angular Material one.
@function mat-typography-config-level-from-mdc($mdc-level) {
  $mdc-level-config: map.get(mdc-typography.$styles, $mdc-level);

  @return typography.define-typography-level(
      map.get($mdc-level-config, font-size),
      map.get($mdc-level-config, line-height),
      map.get($mdc-level-config, font-weight),
      map.get($mdc-level-config, font-family),
      map.get($mdc-level-config, letter-spacing));
}

// Configures MDC's global variables to reflect the given theme, applies the given styles,
// then resets the global variables to prevent unintended side effects.
// stylelint-disable-next-line material/theme-mixin-api
@mixin mat-using-mdc-theme($config) {
  $primary: theming.get-color-from-palette(map.get($config, primary));
  $accent: theming.get-color-from-palette(map.get($config, accent));
  $warn: theming.get-color-from-palette(map.get($config, warn));
  $background-palette: map.get($config, background);

  // Save the original values.
  $orig-primary: mdc-theme-color.$primary;
  $orig-on-primary: mdc-theme-color.$on-primary;
  $orig-secondary: mdc-theme-color.$secondary;
  $orig-on-secondary: mdc-theme-color.$on-secondary;
  $orig-background: mdc-theme-color.$background;
  $orig-surface: mdc-theme-color.$surface;
  $orig-on-surface: mdc-theme-color.$on-surface;
  $orig-error: mdc-theme-color.$error;
  $orig-on-error: mdc-theme-color.$on-error;
  $orig-property-values: mdc-theme-color.$property-values;

  // Set new values based on the given Angular Material theme.
  mdc-theme-color.$primary: $primary;
  mdc-theme-color.$on-primary:
      if(mdc-theme-color.contrast-tone(mdc-theme-color.$primary) == 'dark', #000, #fff);
  mdc-theme-color.$secondary: $accent;
  mdc-theme-color.$on-secondary:
      if(mdc-theme-color.contrast-tone(mdc-theme-color.$secondary) == 'dark', #000, #fff);
  mdc-theme-color.$background: theming.get-color-from-palette($background-palette, background);
  mdc-theme-color.$surface: theming.get-color-from-palette($background-palette, card);
  mdc-theme-color.$on-surface:
      if(mdc-theme-color.contrast-tone(mdc-theme-color.$surface) == 'dark', #000, #fff);
  mdc-theme-color.$error: $warn;
  mdc-theme-color.$on-error:
      if(mdc-theme-color.contrast-tone(mdc-theme-color.$error) == 'dark', #000, #fff);
  mdc-theme-color.$property-values: (
      // Primary
      primary: mdc-theme-color.$primary,
      // Secondary
      secondary: mdc-theme-color.$secondary,
      // Background
      background: mdc-theme-color.$background,
      // Surface
      surface: mdc-theme-color.$surface,
      // Error
      error: mdc-theme-color.$error,
      on-primary: mdc-theme-color.$on-primary,
      on-secondary: mdc-theme-color.$on-secondary,
      on-surface: mdc-theme-color.$on-surface,
      on-error: mdc-theme-color.$on-error,
      // Text-primary on "background" background
      text-primary-on-background:
          mdc-theme-color.ink-color-for-fill_(primary, mdc-theme-color.$background),
      text-secondary-on-background:
          mdc-theme-color.ink-color-for-fill_(secondary, mdc-theme-color.$background),
      text-hint-on-background:
          mdc-theme-color.ink-color-for-fill_(hint, mdc-theme-color.$background),
      text-disabled-on-background:
          mdc-theme-color.ink-color-for-fill_(disabled, mdc-theme-color.$background),
      text-icon-on-background:
          mdc-theme-color.ink-color-for-fill_(icon, mdc-theme-color.$background),
      // Text-primary on "light" background
      text-primary-on-light: mdc-theme-color.ink-color-for-fill_(primary, light),
      text-secondary-on-light: mdc-theme-color.ink-color-for-fill_(secondary, light),
      text-hint-on-light: mdc-theme-color.ink-color-for-fill_(hint, light),
      text-disabled-on-light: mdc-theme-color.ink-color-for-fill_(disabled, light),
      text-icon-on-light: mdc-theme-color.ink-color-for-fill_(icon, light),
      // Text-primary on "dark" background
      text-primary-on-dark: mdc-theme-color.ink-color-for-fill_(primary, dark),
      text-secondary-on-dark: mdc-theme-color.ink-color-for-fill_(secondary, dark),
      text-hint-on-dark: mdc-theme-color.ink-color-for-fill_(hint, dark),
      text-disabled-on-dark: mdc-theme-color.ink-color-for-fill_(disabled, dark),
      text-icon-on-dark: mdc-theme-color.ink-color-for-fill_(icon, dark)
  );

  // Apply given rules.
  @content;

  // Reset the original values.
  mdc-theme-color.$primary: $orig-primary;
  mdc-theme-color.$on-primary: $orig-on-primary;
  mdc-theme-color.$secondary: $orig-secondary;
  mdc-theme-color.$on-secondary: $orig-on-secondary;
  mdc-theme-color.$background: $orig-background;
  mdc-theme-color.$surface: $orig-surface;
  mdc-theme-color.$on-surface: $orig-on-surface;
  mdc-theme-color.$error: $orig-error;
  mdc-theme-color.$on-error: $orig-on-error;
  mdc-theme-color.$property-values: $orig-property-values;
}

// Configures MDC's global variables to reflect the given typography config,
// applies the given styles, then resets the global variables to prevent unintended side effects.
// stylelint-disable-next-line material/theme-mixin-api
@mixin mat-using-mdc-typography($config) {
  // Save the original values.
  $orig-mdc-typography-styles: mdc-typography.$styles;

  // Set new values based on the given Angular Material typography configuration.
  @if $config {
    mdc-typography.$styles: mat-typography-config-to-mdc($config);
  }

  // Apply given rules.
  @content;

  // Reset the original values.
  mdc-typography.$styles: $orig-mdc-typography-styles;
}
